package cromosomas;

import java.util.ArrayList;
import java.util.Arrays;
import util.Aleatorio;




public class Arbol implements Cloneable{
	
	public static enum Tipo { AVANZA,GIRA_DERECHA,GIRA_IZQUIERDA,SIC,PROGN2,PROGN3, BASURA}; 
	private Arbol padre;
	private Arbol[] hijos;
        private int cuentanodos;
	//private int num_hijos;
	private Tipo tipo;
	private int num_nodos;
	private int profundidad;
        public static int pos;
	public Arbol (Arbol pad, Tipo t, int n_nod, int prof){
		padre=pad;
		tipo=t;
		n_nod=num_nodos;
		profundidad=prof;
		//num_hijos = n_hijos;
		//hijos= new Arbol[num_hijos];
	}
	
/*	public boolean addHijo(int i, Arbol hijo){
		if(i>=0 && i<num_hijos ){
			hijos[i]=hijo;
			return true;
		}
		return false;
	}
	
	public boolean isTerminal(){
		return num_hijos==0;
	}*/

    public Arbol[] getHijos() {
        return hijos;
    }

    /*public int getNum_hijos() {
        return num_hijos;
    }*/

    public int getNum_nodos() {
        return num_nodos;
    }

    public Arbol getPadre() {
        return padre;
    }

    public int getProfundidad() {
        return profundidad;
    }

    public Tipo getTipo() {
        return tipo;
    }

    public void setHijos(Arbol[] hijos) {
        this.hijos = hijos;
    }

    /*public void setNum_hijos(int num_hijos) {
        this.num_hijos = num_hijos;
    }*/

    public void setNum_nodos(int num_nodos) {
        this.num_nodos = num_nodos;
    }

    public void setPadre(Arbol padre) {
        this.padre = padre;
    }

    public void setProfundidad(int profundidad) {
        this.profundidad = profundidad;
    }

    public void setTipo(Tipo tipo) {
        this.tipo = tipo;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Arbol other = (Arbol) obj;
        if (this.padre != other.padre && (this.padre == null || !this.padre.equals(other.padre))) {
            return false;
        }
        if (!Arrays.deepEquals(this.hijos, other.hijos)) {
            return false;
        }
        if (this.tipo != other.tipo) {
            return false;
        }
        if (this.num_nodos != other.num_nodos) {
            return false;
        }
        if (this.profundidad != other.profundidad) {
            return false;
        }
        return true;
    }

    @Override
    public Arbol clone(){
        Arbol obj= new Arbol(this.getPadre(),this.getTipo(), this.num_nodos, this.getProfundidad());
        if(this.getHijos()!=null){
        obj.setHijos(this.getHijos().clone());
        }
        obj.setNum_nodos(this.getNum_nodos());
        if(this.getPadre()!=null){
        obj.setPadre(this.getPadre().clone());
        }
        obj.setProfundidad(this.getProfundidad());
        obj.setTipo(this.getTipo());

        return obj;
    }//clone

    @Override
    public String toString() {
        String aux ="(";
        aux+=this.tipo;
        aux+=" nodos: " + this.num_nodos;
        aux+=" profundidad: " + this.profundidad;
        
        if(this.hijos!=null){
            for(int i=0; i<this.hijos.length;i++){
                if(this.hijos[i]!=null){
                aux+=this.hijos[i];
                }
            }
        }
        aux+=")"; 
        return aux;
    }

    public String muestraRecorrido(){
        String aux="";
        if(this.hijos!=null){
            for(int i=0;i<this.hijos.length;i++){
                    aux+=hijos[i].muestraRecorrido();
                
            }//for
        }//if
        else{ //hijos==null;
            aux+=" "+this.tipo;
        }
        return aux;
    }
    
       
    public Arbol getSubArbolInit(boolean enc,int posi,int tope){
        /*
        tope es el nodo total de arboles del arbol
        posi es la posicion a alcanzar
        
        iremos restando valores de tope hasta llegar a posi
        hasta alcanzar el puntero al subarbol a devolver
         * Ejemplo: tope=20; posi=15 
           recorremos el arbol restando hasta:
           20,19,18,17,16,15 y devolveremos el puntero al subarbol
           que referenciamos al llegar ahi
         
         */
        pos=tope; //nodo extremo
        ArrayList solFinal= new ArrayList();
        this.getSubArbol(false,solFinal,posi);
        return (Arbol)solFinal.get(0);
    }
    
    public ArrayList<Arbol> getSubArbol(boolean enc,ArrayList<Arbol> solFinal,int cota){
    
      if(enc==false){
        if(this.hijos!=null && pos>cota){
            for(int i=0;i<this.hijos.length;i++){
                   pos--;
                   if(enc==false){
                        if(pos==cota){       
                            enc=true;      
                            solFinal.add(hijos[i]);
                            return solFinal;
                        }//if
                        else{
                            if(pos>0){ 
                            solFinal.addAll(hijos[i].getSubArbol(enc,solFinal,cota));
                            }
                       }//else
                   }//enc==false                
            }//for
        }//if
        else{ //hijos==null;
            int a=1;
            if(pos <= cota || cota==0){//caso base 1
            solFinal.add(this);
            enc=true;
            return solFinal;
            }//pos==0
            
        }//else
       }//enc=false
        return solFinal;
    
    }
    
   //-------------------set subarbol-------------------------------------
    public Arbol setSubArbolInit(boolean enc,int posi,int tope , Arbol nuevoArbol){
        /*
        igual que el get. Remplazamos el puntero al nodo del subarbol por un puntero
        al nodo inicial del subarbol pasado como parametro
         
         */
        pos=tope; //nodo extremo
        ArrayList solFinal= new ArrayList();
        this.setSubArbol(false,solFinal,posi,nuevoArbol);
        Arbol tt=(Arbol)solFinal.get(0);
        tt= nuevoArbol; //le asignamos el puntero al nuevo arbol
        int a =666;
        return this;
    }

    public ArrayList<Arbol> setSubArbol(boolean enc,ArrayList<Arbol> solFinal,int cota,Arbol nuevoArbol){
    
      if(enc==false){
        if(this.hijos!=null && pos>cota){
            for(int i=0;i<this.hijos.length;i++){
                   pos--;
                   if(enc==false){
                        if(pos==cota){       
                            enc=true;      
                            solFinal.add(hijos[i]);
                            if(solFinal.size()==1){
                             hijos[i]=nuevoArbol;
                            }
                            return solFinal;
                        }//if
                        else{
                            if(pos>0){ 
                            solFinal.addAll(hijos[i].setSubArbol(enc,solFinal,cota,nuevoArbol));
                            }
                       }//else
                   }//enc==false                
            }//for
        }//if
        else{ //hijos==null;
            int a=1;
            if(pos <= cota || cota==0){//caso base 1
            solFinal.add(this);
            if(solFinal.size()==1){
                Arbol []h = new Arbol[1];
                h[0]=nuevoArbol;
                this.hijos=h;
                this.hijos[0].padre=nuevoArbol;
            }
            enc=true;
            return solFinal;
            }//pos==0
            
        }//else
       }//enc=false
        return solFinal;
    
    }
    
    
    
    public void actualizarNumeroNodos(){     
        this.cuentanodos= cuentaNodos();
        
    }
    
    public int cuentaNodos(){
        int cuentanodos=0;
        if(this.hijos!=null){
            cuentanodos++;
            for(int i=0;i<this.hijos.length;i++){
                    cuentanodos+= hijos[i].cuentaNodos();
                
            }//for
        }//if
        else{ //hijos==null;
            
            cuentanodos++;
            
        }
        return cuentanodos;
        
    }//cuentanodos
    
    //-----------Mutacion Terminal--------------
       //-------------------set subarbol-------------------------------------
    public Arbol setSubArbolInitTerminal(boolean enc,int posi,int tope , Arbol nuevoArbol){
        
        
        pos=tope; //nodo extremo
        ArrayList solFinal= new ArrayList();
        this.setSubArbolTerminal(false,solFinal,posi,nuevoArbol);
        Arbol tt=(Arbol)solFinal.get(0);
        /*
         Repetir hasta que efectivamente sea un Terminal
         Lo que se reemplaza
         */
        while(tt.hijos!=null){
            double p2=(double)Aleatorio.getDoubleAleatorio();
            int posi2 = (int) Math.floor(p2 *tope);
            solFinal= new ArrayList();
            this.setSubArbolTerminal(false,solFinal,posi2,nuevoArbol);
            tt=(Arbol)solFinal.get(0);
        }
        return this;
    }

    public ArrayList<Arbol> setSubArbolTerminal(boolean enc,ArrayList<Arbol> solFinal,int cota,Arbol nuevoArbol){
    
      if(enc==false){
        if(this.hijos!=null && pos>cota){
            for(int i=0;i<this.hijos.length;i++){
                   pos--;
                   if(enc==false){
                        if(pos==cota){       
                            enc=true;      
                            solFinal.add(hijos[i]);
                            return solFinal;
                        }//if
                        else{
                            if(pos>0){ 
                            solFinal.addAll(hijos[i].setSubArbolTerminal(enc,solFinal,cota,nuevoArbol));
                            }
                       }//else
                   }//enc==false                
            }//for
        }//if
        else{ //hijos==null;
            int a=1;
            if(pos <= cota || cota==0){//caso base 1
            solFinal.add(this);
            if(solFinal.size()==1){
                Arbol []h = new Arbol[1];
                h[0]=nuevoArbol;
                this.hijos=h;
                hijos[0].padre=nuevoArbol;
            }
            enc=true;
            return solFinal;
            }//pos==0
            
        }//else
       }//enc=false
        return solFinal;
    
    }
    
        public Arbol setSubArbolInitFuncion(boolean enc,int posi,int tope){
        
        pos=tope; //nodo extremo
        ArrayList solFinal= new ArrayList();
        this.setSubArbolFuncion(false,solFinal,posi);
        Arbol tt=(Arbol)solFinal.get(0);
        /*
         Repetir hasta que efectivamente sea una Funcion
         Lo que se reemplaza
         */
        while(tt.hijos==null){
            double p2=(double)Aleatorio.getDoubleAleatorio();
            int posi2 = (int) Math.floor(p2 *tope);
            solFinal= new ArrayList();
            this.setSubArbolFuncion(false,solFinal,posi2);
            tt=(Arbol)solFinal.get(0);
        }
        return this;
    }

    public ArrayList<Arbol> setSubArbolFuncion(boolean enc,ArrayList<Arbol> solFinal,int cota){
    
      if(enc==false){
        if(this.hijos!=null && pos>cota){
            for(int i=0;i<this.hijos.length;i++){
                   pos--;
                   if(enc==false){
                        if(pos==cota){       
                            enc=true;      
                            solFinal.add(hijos[i]);
                            if(solFinal.size()==1){
                                if(hijos[i].tipo == Arbol.Tipo.SIC){
                                    hijos[i].tipo= Arbol.Tipo.PROGN2;
                                }
                                if(hijos[i].tipo == Arbol.Tipo.PROGN2){
                                    hijos[i].tipo= Arbol.Tipo.SIC;
                                }
                                
                            }
                            return solFinal;
                        }//if
                        else{
                            if(pos>0){ 
                            solFinal.addAll(hijos[i].setSubArbolFuncion(enc,solFinal,cota));
                            }
                       }//else
                   }//enc==false                
            }//for
        }//if
        else{ //hijos==null;
            
            if(pos <= cota || cota==0){//caso base 1
            solFinal.add(this);
            enc=true;
            return solFinal;
            }//pos==0
            
        }//else
       }//enc=false
        return solFinal;
    
    }
    
    
}
